<?php

namespace app\common\library;


use think\Exception;

/**
 * 支付宝类
 */
class Alipay
{
    /**
     * 接口地址
     */
    const API_URL = 'https://openapi.alipay.com/gateway.do';
    /**
     * 应用ID
     * @var string
     */
    private $appId;
    /**
     * 支付私钥
     * @var string
     */
    private $privKey;
    /**
     * 支付公钥
     * @var string
     */
    private $pubKey;
    /**
     * 支付宝公钥
     * @var
     */
    private $alipayKey;
    public $format = 'json';
    public $charset = 'utf-8';
    public $signType = 'RSA2';
    public $notifyUrl;
    public $timestamp;
    public $version = '1.0';
    public $payMmethod = "alipay.trade.app.pay";
    public function __construct()
    {
        $this->timestamp = date('Y-m-d H:i:s', time());
        $this->appId = \think\config::get("site.alipayappid");
        $this->privKey = \think\config::get("site.alipayprivkey");
        $this->pubKey = \think\config::get("site.alipaypubkey");
        $this->notifyUrl = request()->domain()."api/notify/alipay";
    }
    /**
     * @param Request $request
     * @return mixed
     * @throws Exception
     */
    public function run($data) {
        try {
            if(empty($this->appId) || empty($this->privKey) || empty($this->pubKey)){
                throw new Exception("参数错误");
            }
            $sysParams = [
                'app_id' => $this->appId,
                'method' => $this->payMmethod,
                'format' => $this->format,
                'charset' => $this->charset,
                'sign_type' => $this->signType,
                'timestamp' => (string)$this->timestamp,
                'version' => $this->version,
                'notify_url' => $this->notifyUrl,
            ];
            $sysParams['biz_content'] = json_encode($data, JSON_UNESCAPED_UNICODE);
            $sysParams['sign'] = $this->sign($sysParams);
            return $this->urlEncodeArray($sysParams);
        }catch (\Exception $e){
            throw new \Exception('支付宝下单失败:'.$e->getMessage());
        }
    }
    /**
     * @param array $params
     * @return mixed
     */
    private function urlEncodeArray($params) {
        ksort($params);
        $stringToBeSigned = '';
        $i = 0;
        foreach ($params as $k => $v) {
            if (!empty($v) && substr($v, 0, 1) != '@') {
                if ($i == 0) {
                    $stringToBeSigned .= "$k"."=".urlencode($v);
                } else {
                    $stringToBeSigned .= "&"."$k"."=".urlencode($v);
                }
            }
            $i++;
        }
        unset ($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 验签
     * @param array $params
     * @param $sign
     * @return string
     */
    public function checkSign($params, $sign) {
        $str = chunk_split($this->alipayKey, 64, "\n");
        $key = "-----BEGIN PUBLIC KEY-----\n$str-----END PUBLIC KEY-----\n";
        return (bool)openssl_verify(json_encode($params), base64_decode($sign), $key, OPENSSL_ALGO_SHA256);
    }

    /**
     * 签名 RSA2
     * @param array $params
     * @return string
     */
    private function sign($params = []) {
        $stringToBeSigned = $this->getSignContent($params);
        $str = chunk_split($this->privKey, 64, "\n");
        $key = "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n";
        openssl_sign($stringToBeSigned, $sign, $key, OPENSSL_ALGO_SHA256);
        return base64_encode($sign);
    }

    /**
     * 签名字符串
     * @param array $params
     * @return string
     */
    private function getSignContent($params) {
        ksort($params);
        $stringToSign = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (empty($params[$k])) {
                continue;
            }
            if ($i == 0) {
                $stringToSign .= "$k"."="."$v";
            } else {
                $stringToSign .= "&"."$k"."="."$v";
            }
            $i++;
        }
        unset ($k, $v);
        return $stringToSign;
    }
}
